package code.google.model.upnp;

import java.net.URI;
import java.util.ArrayList;

import org.teleal.cling.UpnpService;
import org.teleal.cling.android.AndroidUpnpService;
import org.teleal.cling.controlpoint.ActionCallback;
import org.teleal.cling.controlpoint.ControlPoint;
import org.teleal.cling.model.action.ActionArgumentValue;
import org.teleal.cling.model.action.ActionInvocation;
import org.teleal.cling.model.message.UpnpResponse;
import org.teleal.cling.model.meta.Device;
import org.teleal.cling.model.meta.LocalDevice;
import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.model.meta.Service;
import org.teleal.cling.model.types.InvalidValueException;
import org.teleal.cling.model.types.ServiceId;
import org.teleal.cling.model.types.UDAServiceId;
import org.teleal.cling.registry.DefaultRegistryListener;
import org.teleal.cling.registry.Registry;

import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
import code.google.model.util.interfaces.DeviceListObserver;
import code.google.model.util.interfaces.IUpnpNode;
// TODO Check if singleton pattern is the right way to get access or if it is necessary to use the ModelProvider
public class UpnpNode implements ServiceConnection, IUpnpNode {

	private static UpnpNode instance = null;
	private AndroidUpnpService upnpService;
	private ArrayList<Device> deviceList;
	private ArrayList<DeviceListObserver> observerList = new ArrayList<DeviceListObserver>();
	private BrowseRegistryListener registryListener = new BrowseRegistryListener();
	
	private UpnpNode() {
		deviceList = new ArrayList<Device>();
	}

	public static UpnpNode getInstance() {
		if (instance == null) {
			instance = new UpnpNode();
		}
		return instance;
		
	}

	@Override
	public void onServiceConnected(ComponentName className, IBinder service) {
		upnpService = (AndroidUpnpService) service;
		// Refresh the list
		deviceList.clear();
		for (Device device : upnpService.getRegistry().getDevices()) {
			registryListener.deviceAdded(device);
		}
		
		// Getting ready for future device advertisements
        upnpService.getRegistry().addListener(registryListener);

        // Search asynchronously for all devices
        upnpService.getControlPoint().search();

	}

	@Override
	public void onServiceDisconnected(ComponentName arg0) {
		upnpService = null;
	}

	public void refresh(){
		if(upnpService != null){
			upnpService.getRegistry().removeAllRemoteDevices();
			upnpService.getControlPoint().search();
		}
	}
	
	class BrowseRegistryListener extends DefaultRegistryListener {
		ServiceId serviceId = new UDAServiceId("AVTransport");
		Service avTransport;

		@Override
		public void remoteDeviceDiscoveryStarted(Registry registry,
				RemoteDevice device) {
			deviceAdded(device);
		}

		@Override
		public void remoteDeviceDiscoveryFailed(Registry registry,
				final RemoteDevice device, final Exception ex) {
			Log.w("UpnpNode", "Failed to discover remote device: "+ex.getMessage());
			deviceRemoved(device);
		}

		@Override
		public void remoteDeviceAdded(Registry registry, RemoteDevice device) {
//			if ((avTransport = device.findService(serviceId)) != null) {
				deviceAdded(device);
//			}
		}

		@Override
		public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
			deviceRemoved(device);
		}

		@Override
		public void localDeviceAdded(Registry registry, LocalDevice device) {
			deviceAdded(device);
		}

		@Override
		public void localDeviceRemoved(Registry registry, LocalDevice device) {
			deviceRemoved(device);
		}

		public void deviceAdded(final Device device) {
			int position = deviceList.indexOf(device);
			if (position >= 0) {
				// Device already in the list, re-set new value at same position
				deviceList.remove(device);
				deviceList.add(position, device);
			} else {
				
			}
			for(DeviceListObserver o : observerList){
				o.deviceChanged(1, device);
			}
		}

		public void deviceRemoved(final Device device) {
			deviceList.remove(device);
			deviceList.add(device);
			for(DeviceListObserver o : observerList){
				o.deviceChanged(0, device);
			}
		}
		
//		void executeAction (UpnpService upnpService, Service switchPowerService) {
//			ActionInvocation setTargetInvocation = new SetTargetActionInvocation(switchPowerService);
//			
//			//Executes asynchronous in the backround
//			upnpService.getControlPoint().execute(
//					new ActionCallback(setTargetInvocation) {
//						@Override
//						public void success(ActionInvocation invocation){
//							assert invocation.getOutput().length == 0;
//							Log.d("UpnpNode", "Successfully switched light");
//						}
//						
//						@Override
//						public void failure(ActionInvocation invocation,
//								UpnpResponse operation,
//								String defaultMsg) {
//							Log.e("UpnpNode", defaultMsg);
//						}
//
//
//					}
//					
//					
//					);
//		}
//		
//		class SetTargetActionInvocation extends ActionInvocation {
//			SetTargetActionInvocation(Service service) {
//				super(service.getAction("SetTarget"));
//				
//				try {
//					//Throws InvalidValueException if the value is of wrong type
//					setInput("NewTargetValue", true);
//				} catch (InvalidValueException ex) {
//					Log.d("UpnpNode", "Shutting down");
//					Log.d("UpnpNode", ex.getMessage());
//					System.exit(1);
//				}
//			}
//		}
	}













	@Override
	public ControlPoint getControlPoint() {
		return upnpService.getControlPoint();
	}

	@Override
	public void addDeviceListObserver(DeviceListObserver observer) {
		observerList.add(observer);
		for(Device d : deviceList){
			observer.deviceChanged(1, d);
		}
		
	}


}
